// Project: Salient
// http://salient.codeplex.com
// 
// Copyright 2010, Sky Sanders <sky at skysanders.net>
// Dual licensed under the MIT or GPL Version 2 licenses.
// http://salient.codeplex.com/license
// 
// Date: April 25 2010 

#region

using System;
using System.Text;
using Salient.Collections.Generic;

#endregion

namespace Salient.Data.Common
{
    ///<summary>
    ///</summary>
    public class ConnectionStringParser : SynonymousDictionary<string>
    {
        private const char DQuote = '"';
        private const char Equal = '=';
        private const char Semi = ';';
        private const char Space = ' ';
        private const char SQuote = '\'';

        /// <summary>
        /// 
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        protected virtual string GetValue(string key, string value)
        {
            return value;
        }


        ///<summary>
        ///</summary>
        ///<param name="input"></param>
        public void Parse(string input)
        {
            if (input.Length == 0)
                return;

            input += Semi;

            bool inSingleQuote = false;
            bool inDoubleQuotes = false;
            bool inKey = true;

            string key = String.Empty;
            StringBuilder sb = new StringBuilder();

            for (int i = 0; i < input.Length; i += 1)
            {
                char c = input[i];

                char peek = i == input.Length - 1 ? '\0' : input[i + 1];


                switch (c)
                {
                    case SQuote:
                        if (inDoubleQuotes)
                        {
                            sb.Append(c);
                        }
                        else if (peek.Equals(c))
                        {
                            sb.Append(c);
                            i += 1;
                        }
                        else
                        {
                            inSingleQuote = !inSingleQuote;
                        }
                        break;

                    case DQuote:
                        if (inSingleQuote)
                        {
                            sb.Append(c);
                        }
                        else if (peek.Equals(c))
                        {
                            sb.Append(c);
                            i += 1;
                        }
                        else
                        {
                            inDoubleQuotes = !inDoubleQuotes;
                        }
                        break;

                    case Semi:
                        if (inDoubleQuotes || inSingleQuote)
                        {
                            sb.Append(c);
                        }
                        else
                        {
                            if (!string.IsNullOrEmpty(key))
                            {
                                this[key] = GetValue(key, sb.ToString());
                            }
                            inKey = true;
                            key = String.Empty;
                            sb = new StringBuilder();
                        }
                        break;

                    case Equal:
                        if (inDoubleQuotes || inSingleQuote || !inKey)
                        {
                            sb.Append(c);
                        }
                        else if (peek.Equals(c))
                        {
                            sb.Append(c);
                            i += 1;
                        }
                        else
                        {
                            key = GetKey(sb.ToString());
                            sb = new StringBuilder();
                            inKey = false;
                        }
                        break;

                    case Space:
                        if (inSingleQuote || inDoubleQuotes)
                        {
                            sb.Append(c);
                        }
                        else if (sb.Length > 0 && !peek.Equals(Semi))
                        {
                            sb.Append(c);
                        }
                        break;

                    default:
                        sb.Append(c);
                        break;
                }
            }
        }


        /// <summary>
        /// Returns a <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
        /// </summary>
        /// <returns>
        /// A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
        /// </returns>
        /// <filterpriority>2</filterpriority>
        public override string ToString()
        {
            StringBuilder normalized = new StringBuilder();
            foreach (string key in Keys)
            {
                string entry = String.Format("{0}=\"{1}\";", key, this[key].Replace("\"", "\"\""));
                normalized.Append(entry);
            }
            return normalized.ToString();
        }
    }
}